<html>
  <head>
    <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Spring_4 JdbcTemplate、事务控制 | 清汤牛肉锅</title>
<meta name="description" content="温故而知新
" />
<link rel="shortcut icon" href="https://ArtZoick.github.io//favicon.ico?v=1572228390311">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.10.0/katex.min.css">
<link rel="stylesheet" href="https://ArtZoick.github.io//styles/main.css">

<script src="https://cdn.bootcss.com/highlight.js/9.12.0/highlight.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Droid+Serif:400,700">


<script async src="https://www.googletagmanager.com/gtag/js?id=UA-143265163-1"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-143265163-1');
</script>


  </head>
  <body>
    <div class="main">
      <div class="main-content">
        <div class="site-header">
  <a href="https://ArtZoick.github.io/">
  <img class="avatar" src="https://ArtZoick.github.io//images/avatar.png?v=1572228390311" alt="">
  </a>
  <h1 class="site-title">
    清汤牛肉锅
  </h1>
  <p class="site-description">
    温故而知新

  </p>
  <div class="menu-container">
    
      
        <a href="/" class="menu">
          首页
        </a>
      
    
      
        <a href="/archives" class="menu">
          归档
        </a>
      
    
      
        <a href="/tags" class="menu">
          标签
        </a>
      
    
      
        <a href="/post/about" class="menu">
          关于
        </a>
      
    
      
        <a href="/post/beef" class="menu">
          牛肉锅
        </a>
      
    
  </div>
  <div class="social-container">
    
      
    
      
    
      
    
      
    
      
    
  </div>
</div>

        <div class="post-detail">
          <article class="post">
            <h2 class="post-title">
              Spring_4 JdbcTemplate、事务控制
            </h2>
            <div class="post-info">
              <span>
                2019-08-10
              </span>
              <span>
                27 min read
              </span>
              
                <a href="https://ArtZoick.github.io//tag/VwirVE4DD" class="post-tag">
                  # Spring
                </a>
              
                <a href="https://ArtZoick.github.io//tag/framework" class="post-tag">
                  # 框架
                </a>
              
            </div>
            
            <div class="post-content-wrapper">
              <div class="post-content">
                <h2 id="一-spring-中的-jdbctemplate">一、Spring 中的 JdbcTemplate</h2>
<h3 id="1-jdbctemplate-概述">1、 JdbcTemplate 概述</h3>
<p>它是 spring 框架中提供的一个对象，是对原始 Jdbc API 对象的简单封装。</p>
<h3 id="2-jdbctemplate-对象的创建">2、JdbcTemplate 对象的创建</h3>
<p>可以参考它的源码，来一探究竟：</p>
<pre><code class="language-java">public JdbcTemplate() {
}

public JdbcTemplate(DataSource dataSource) {
	setDataSource(dataSource);
	afterPropertiesSet();
}

public JdbcTemplate(DataSource dataSource, boolean lazyInit) {
	setDataSource(dataSource);
	setLazyInit(lazyInit);
	afterPropertiesSet();
}
</code></pre>
<p>除了默认构造函数之外，都需要提供一个数据源。既然有set方法，依据依赖注入，我们可以在配置文件中配置这些对象。</p>
<h3 id="3-jdbctemplate-的增删改查操作">3、JdbcTemplate 的增删改查操作</h3>
<h4 id="1-导入坐标">(1) 导入坐标</h4>
<pre><code class="language-xml">&lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-context&lt;/artifactId&gt;
            &lt;version&gt;5.1.6.RELEASE&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-jdbc&lt;/artifactId&gt;
            &lt;version&gt;5.1.6.RELEASE&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework&lt;/groupId&gt;
            &lt;artifactId&gt;spring-tx&lt;/artifactId&gt;
            &lt;version&gt;5.1.6.RELEASE&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;mysql&lt;/groupId&gt;
            &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
            &lt;version&gt;8.0.15&lt;/version&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
</code></pre>
<h4 id="2-在-spring-配置文件beanxml中配置-jdbctemplate">(2) 在 spring 配置文件bean.xml中配置 JdbcTemplate</h4>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;

    &lt;!--配置数据库的操作模板JdbcTemplate--&gt;
    &lt;bean id=&quot;jdbcTemplate&quot; class=&quot;org.springframework.jdbc.core.JdbcTemplate&quot;&gt;
        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;

    &lt;!--配置Spring内置数据源--&gt;
    &lt;bean id=&quot;dataSource&quot; class=&quot;org.springframework.jdbc.datasource.DriverManagerDataSource&quot;&gt;
        &lt;property name=&quot;driverClassName&quot; value=&quot;com.mysql.cj.jdbc.Driver&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;url&quot; value=&quot;jdbc:mysql://localhost:3306/eesy&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;username&quot; value=&quot;root&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;password&quot; value=&quot;root&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;

&lt;/beans&gt;
</code></pre>
<h4 id="3-最基本的crud使用">(3) 最基本的CRUD使用</h4>
<pre><code class="language-java">/**
 * JdbcTemplate的最基本用法
 */
public class JdbcTemplateDemo2 {

    public static void main(String[] args) {
        //1 获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext(&quot;bean.xml&quot;);
        //2 获取对象
        JdbcTemplate jt = ac.getBean(&quot;jdbcTemplate&quot;,JdbcTemplate.class);
        //3.1 执行操作
        jt.execute(&quot;insert into account(name,money)values('ddd',2222)&quot;);
        
        //3.2 保存
		jt.update(&quot;insert into account(name,money)values(?,?)&quot;,&quot;fff&quot;,5000);
        
        //3.3 修改
		jt.update(&quot;update account set money = money-? where id = ?&quot;,300,6);
        
        //3.4 删除
		jt.update(&quot;delete from account where id = ?&quot;,6);

    }
}
</code></pre>
<h4 id="4-查询所有操作">(4) 查询所有操作</h4>
<pre><code class="language-javascript">public class JdbcTemplateDemo3 {

    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext(&quot;bean.xml&quot;);
        //2.获取对象
        JdbcTemplate jt = ac.getBean(&quot;jdbcTemplate&quot;,JdbcTemplate.class);

        //查询所有
        //3.1 自己写的AccountRowMapper
          List&lt;Account&gt; accounts = jt.query(&quot;select * from account where money &gt; ?&quot;,new 	      AccountRowMapper(),1000f);
        //3.2 spring中的
          List&lt;Account&gt; accounts = jt.query(&quot;select * from account where money &gt; ?&quot;,new BeanPropertyRowMapper&lt;Account&gt;(Account.class),1000f);
          for(Account account : accounts){
              System.out.println(account);
          }
        
    }
}



/**
 * 匿名内部类
 * 定义Account的封装策略
 */
class AccountRowMapper implements RowMapper&lt;Account&gt;{
    /**
     * 把结果集中的数据封装到Account中，然后由spring把每个Account加到集合中
     * @param rs
     * @param rowNum
     * @return
     * @throws SQLException
     */
    @Override
    public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
        Account account = new Account();
        account.setId(rs.getInt(&quot;id&quot;));
        account.setName(rs.getString(&quot;name&quot;));
        account.setMoney(rs.getFloat(&quot;money&quot;));
        return account;
    }
}
</code></pre>
<h4 id="5-查询一个">(5) 查询一个</h4>
<pre><code class="language-java">//查询一个
        List&lt;Account&gt; accounts = jt.query(&quot;select * from account where id = ?&quot;,new BeanPropertyRowMapper&lt;Account&gt;(Account.class),1);
        System.out.println(accounts.isEmpty()?&quot;没有内容&quot;:accounts.get(0));
</code></pre>
<h4 id="6-查询返回一行一列数据">(6) 查询返回一行一列数据</h4>
<pre><code class="language-java">//查询返回一行一列（使用聚合函数，但不加group by子句）
        Long count = jt.queryForObject(&quot;select count(*) from account where money &gt; ?&quot;,Long.class,1000f);
        System.out.println(count);
</code></pre>
<h3 id="4-在-dao-中使用-jdbctemplate">4、在 dao 中使用 JdbcTemplate</h3>
<h4 id="1-实体类">(1) 实体类</h4>
<pre><code class="language-java">@Getter
@Setter
@ToString
public class Account implements Serializable {

    private Integer id;
    private String name;
    private Float money;

}
</code></pre>
<h4 id="2-iaccountdao接口">(2) IAccountDao接口</h4>
<pre><code class="language-java">/**
 * 账户的持久层接口
 */
public interface IAccountDao {

    /**
     * 根据Id查询账户
     * @param accountId
     * @return
     */
    Account findAccountById(Integer accountId);

    /**
     * 根据名称查询账户
     * @param accountName
     * @return
     */
    Account findAccountByName(String accountName);

    /**
     * 更新账户
     * @param account
     */
    void updateAccount(Account account);
}
</code></pre>
<h4 id="3-第一种方法实现类中定义-jdbctemplate">(3) 第一种方法：实现类中定义 JdbcTemplate</h4>
<pre><code class="language-java">/**
 * 账户的持久层实现类
 */
@Repository
public class AccountDaoImpl2 implements IAccountDao {

    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }


    @Override
    public Account findAccountById(Integer accountId) {
        List&lt;Account&gt; accounts = jdbcTemplate.query(&quot;select * from account where id = ?&quot;,new BeanPropertyRowMapper&lt;Account&gt;(Account.class),accountId);
        return accounts.isEmpty()?null:accounts.get(0);
    }

    @Override
    public Account findAccountByName(String accountName) {
        List&lt;Account&gt; accounts = jdbcTemplate.query(&quot;select * from account where name = ?&quot;,new BeanPropertyRowMapper&lt;Account&gt;(Account.class),accountName);
        if(accounts.isEmpty()){
            return null;
        }
        if(accounts.size()&gt;1){
            throw new RuntimeException(&quot;结果集不唯一&quot;);
        }
        return accounts.get(0);
    }

    @Override
    public void updateAccount(Account account) {
        jdbcTemplate.update(&quot;update account set name=?,money=? where id=?&quot;,account.getName(),account.getMoney(),account.getId());
    }
}
</code></pre>
<p>此处需要将账户的持久层配置，即bean.xml中</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;

    &lt;!--配置账户的持久层--&gt;
    &lt;bean id=&quot;accountDao&quot; class=&quot;top.zoick.dao.impl.AccountDaoImpl2&quot;&gt;
        &lt;property name=&quot;jdbcTemplate&quot; ref=&quot;jdbcTemplate&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;

    &lt;!--配置数据库的操作模板JdbcTemplate--&gt;
    &lt;bean id=&quot;jdbcTemplate&quot; class=&quot;org.springframework.jdbc.core.JdbcTemplate&quot;&gt;
        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;

    &lt;!--配置Spring内置数据源--&gt;
    &lt;bean id=&quot;dataSource&quot; class=&quot;org.springframework.jdbc.datasource.DriverManagerDataSource&quot;&gt;
        &lt;property name=&quot;driverClassName&quot; value=&quot;com.mysql.cj.jdbc.Driver&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;url&quot; value=&quot;jdbc:mysql://localhost:3306/eesy&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;username&quot; value=&quot;root&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;password&quot; value=&quot;root&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;

&lt;/beans&gt;
</code></pre>
<p>测试</p>
<pre><code class="language-java">public class JdbcTemplateDemo4 {

    public static void main(String[] args) {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext(&quot;bean.xml&quot;);
        //2.获取对象
        IAccountDao accountDao = ac.getBean(&quot;accountDao&quot;, IAccountDao.class);

        Account account = accountDao.findAccountById(1);
        System.out.println(account);

        account.setMoney(30000f);
        accountDao.updateAccount(account);
    }
}
</code></pre>
<p><strong>注意:</strong><br>
此种方式dao 有很多时，每个 dao 都有一些重复性的代码。下面就是重复代码：</p>
<pre><code class="language-java">private JdbcTemplate jdbcTemplate;

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
　　this.jdbcTemplate = jdbcTemplate;
}
</code></pre>
<h4 id="4-第二种方法实现类继承-jdbcdaosupport">(4) 第二种方法：实现类继承 JdbcDaoSupport</h4>
<pre><code class="language-java">public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {

    @Override
    public Account findAccountById(Integer accountId) {
        
        //getJdbcTemplate()方法是从父类上继承下来的。
        List&lt;Account&gt; accounts = super.getJdbcTemplate().query(&quot;select * from account where id = ?&quot;,new BeanPropertyRowMapper&lt;Account&gt;(Account.class),accountId);
        return accounts.isEmpty()?null:accounts.get(0);
    }

    @Override
    public Account findAccountByName(String accountName) {
        List&lt;Account&gt; accounts = super.getJdbcTemplate().query(&quot;select * from account where name = ?&quot;,new BeanPropertyRowMapper&lt;Account&gt;(Account.class),accountName);
        if(accounts.isEmpty()){
            return null;
        }
        if(accounts.size()&gt;1){
            throw new RuntimeException(&quot;结果集不唯一&quot;);
        }
        return accounts.get(0);
    }

    @Override
    public void updateAccount(Account account) {
        super.getJdbcTemplate().update(&quot;update account set name=?,money=? where id=?&quot;,account.getName(),account.getMoney(),account.getId());
    }
}

</code></pre>
<p>此处的配置为</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;

    &lt;!--配置账户的持久层--&gt;
    &lt;bean id=&quot;accountDao&quot; class=&quot;top.zoick.dao.impl.AccountDaoImpl&quot;&gt;
        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;

    &lt;!--配置Spring内置数据源--&gt;
    &lt;bean id=&quot;dataSource&quot; class=&quot;org.springframework.jdbc.datasource.DriverManagerDataSource&quot;&gt;
        &lt;property name=&quot;driverClassName&quot; value=&quot;com.mysql.cj.jdbc.Driver&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;url&quot; value=&quot;jdbc:mysql://localhost:3306/eesy&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;username&quot; value=&quot;root&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;password&quot; value=&quot;root&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;
&lt;/beans&gt;
</code></pre>
<p>测试同上</p>
<h2 id="二-spring-中的事务控制">二、Spring 中的事务控制</h2>
<h3 id="1-spring-事务控制需要明确的">1、Spring 事务控制需要明确的</h3>
<p>第一：JavaEE 体系进行分层开发，事务处理位于业务层，Spring 提供了分层设计 业务层的事务处理解决方<br>
案。<br>
第二：spring 框架为我们提供了一组事务控制的接口。具体在后面的第二小节介绍。这组接口是在spring-tx-5.1.6.RELEASE.jar 中。<br>
第三：spring 的事务控制都是基于 AOP 的，它既可以使用编程的方式实现，也可以使用配置的方式实现。我<br>
们学习的重点是使用配置的方式实现。</p>
<h3 id="2-spring-中事务控制的-api-介绍">2、Spring 中事务控制的 API 介绍</h3>
<h4 id="1-platformtransactionmanager">(1) PlatformTransactionManager</h4>
<p>此接口是 spring 的事务管理器，它里面提供了我们常用的操作事务的方法，如下图：</p>
<figure data-type="image" tabindex="1"><img src="https://ArtZoick.github.io//post-images/spring/3.png" alt=""></figure>
<p><strong>真正管理事务的对象:</strong><br>
org.springframework.jdbc.datasource.DataSourceTransactionManager 使用 Spring JDBC 或 或 iBatis 进行持久化数据时使用<br>
org.springframework.orm.hibernate5.HibernateTransactionManager 使用Hibernate 版本进行持久化数据时使用</p>
<h4 id="2-transactiondefinition">(2) TransactionDefinition</h4>
<p>它是事务的定义信息对象，里面有如下方法：</p>
<figure data-type="image" tabindex="2"><img src="https://ArtZoick.github.io//post-images/spring/4.png" alt=""></figure>
<h5 id="a-事务的隔离级别">a、事务的隔离级别</h5>
<figure data-type="image" tabindex="3"><img src="https://ArtZoick.github.io//post-images/spring/5.png" alt=""></figure>
<h5 id="b-事务的传播行为">b、事务的传播行为</h5>
<p><strong>REQUIRED:</strong> 如果当前没有事务，就新建一个事务，如果已经存在一个事务中，加入到这个事务中。一般的选<br>
择（默认值）<br>
<strong>SUPPORTS:</strong> 支持当前事务，如果当前没有事务，就以非事务方式执行（没有事务）<br>
MANDATORY：使用当前的事务，如果当前没有事务，就抛出异常<br>
REQUERS_NEW: 新建事务，如果当前在事务中，把当前事务挂起。<br>
NOT_SUPPORTED: 以非事务方式执行操作，如果当前存在事务，就把当前事务挂起<br>
NEVER: 以非事务方式运行，如果当前存在事务，抛出异常<br>
NESTED: 如果当前存在事务，则在嵌套事务内执行。如果当前没有事务，则执行 REQUIRED 类似的操作。</p>
<h5 id="c-超时时间">c、超时时间</h5>
<p>默认值是-1，没有超时限制。如果有，以秒为单位进行设置。</p>
<h5 id="d-是否是只读事务">d、是否是只读事务</h5>
<p>建议查询时设置为只读。</p>
<h4 id="3-transactionstatus">(3) TransactionStatus</h4>
<p>此接口提供的是事务具体的运行状态，方法介绍如下图：</p>
<figure data-type="image" tabindex="4"><img src="https://ArtZoick.github.io//post-images/spring/6.png" alt=""></figure>
<h3 id="3-基于-xml-的声明式事务控制配置方式-重点">3、基于 XML 的声明式事务控制（配置方式 ）重点</h3>
<h4 id="项目结构">项目结构</h4>
<figure data-type="image" tabindex="5"><img src="https://ArtZoick.github.io//post-images/spring/7.png" alt=""></figure>
<h4 id="1-导入坐标-2">(1) 导入坐标</h4>
<pre><code class="language-xml">&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework&lt;/groupId&gt;
        &lt;artifactId&gt;spring-context&lt;/artifactId&gt;
        &lt;version&gt;5.1.6.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;!--需要jdbc和tx--&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework&lt;/groupId&gt;
        &lt;artifactId&gt;spring-jdbc&lt;/artifactId&gt;
        &lt;version&gt;5.1.6.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework&lt;/groupId&gt;
        &lt;artifactId&gt;spring-tx&lt;/artifactId&gt;
        &lt;version&gt;5.1.6.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.aspectj&lt;/groupId&gt;
        &lt;artifactId&gt;aspectjweaver&lt;/artifactId&gt;
        &lt;version&gt;1.9.2&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework&lt;/groupId&gt;
        &lt;artifactId&gt;spring-aop&lt;/artifactId&gt;
        &lt;version&gt;5.1.6.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework&lt;/groupId&gt;
        &lt;artifactId&gt;spring-test&lt;/artifactId&gt;
        &lt;version&gt;5.1.6.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;mysql&lt;/groupId&gt;
        &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
        &lt;version&gt;8.0.15&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;junit&lt;/groupId&gt;
        &lt;artifactId&gt;junit&lt;/artifactId&gt;
        &lt;version&gt;4.12&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
        &lt;artifactId&gt;lombok&lt;/artifactId&gt;
        &lt;version&gt;1.18.4&lt;/version&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;
</code></pre>
<h4 id="2-数据库与实体类">(2) 数据库与实体类</h4>
<p>数据库</p>
<pre><code class="language-sql">-- ----------------------------
-- Table structure for account
-- ----------------------------
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `money` float NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of account
-- ----------------------------
INSERT INTO `account` VALUES (1, 'aaa', 900);
INSERT INTO `account` VALUES (2, 'bbb', 1100);

SET FOREIGN_KEY_CHECKS = 1;
</code></pre>
<p>实体类</p>
<pre><code class="language-java">@Getter
@Setter
@ToString
public class Account implements Serializable {

    private Integer id;
    private String name;
    private Float money;

}
</code></pre>
<h4 id="3-业务层接口和实现类">(3) 业务层接口和实现类</h4>
<p>IAccountService接口</p>
<pre><code class="language-java">/**
 * @author zoick
 * @date 2019/8/8 15:49
 * 账户的业务层接口
 */
public interface IAccountService {
    /**
     * 根据id查询账户信息
     * @param accountId
     * @return
     */
    Account findAccountById(Integer accountId);

    /**
     * 转账
     * @param sourceName    转成账户名称
     * @param targetName    转入账户名称
     * @param money         转账金额
     */
    void transfer(String sourceName,String targetName,Float money);
}

</code></pre>
<p>接口的实现类</p>
<pre><code class="language-java">/**
 * 账户的业务层实现类
 * 事务控制应该都是在业务层
 */
public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);

    }



    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        System.out.println(&quot;transfer....&quot;);
            //2.1根据名称查询转出账户
            Account source = accountDao.findAccountByName(sourceName);
            //2.2根据名称查询转入账户
            Account target = accountDao.findAccountByName(targetName);
            //2.3转出账户减钱
            source.setMoney(source.getMoney()-money);
            //2.4转入账户加钱
            target.setMoney(target.getMoney()+money);
            //2.5更新转出账户
            accountDao.updateAccount(source);

//            int i=1/0;

            //2.6更新转入账户
            accountDao.updateAccount(target);
    }
}

</code></pre>
<h4 id="4-持久层接口与实现类">(4) 持久层接口与实现类</h4>
<p>IAccountDao接口</p>
<pre><code class="language-java">/**
 * 账户的持久层接口
 */
public interface IAccountDao {

    /**
     * 根据Id查询账户
     * @param accountId
     * @return
     */
    Account findAccountById(Integer accountId);

    /**
     * 根据名称查询账户
     * @param accountName
     * @return
     */
    Account findAccountByName(String accountName);

    /**
     * 更新账户
     * @param account
     */
    void updateAccount(Account account);
}

</code></pre>
<p>接口的实现类</p>
<pre><code class="language-java">/**
 * 账户的持久层实现类
 */
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {



    @Override
    public Account findAccountById(Integer accountId) {
        List&lt;Account&gt; accounts = super.getJdbcTemplate().query(&quot;select * from account where id = ?&quot;,new BeanPropertyRowMapper&lt;Account&gt;(Account.class),accountId);
        return accounts.isEmpty()?null:accounts.get(0);
    }

    @Override
    public Account findAccountByName(String accountName) {
        List&lt;Account&gt; accounts = super.getJdbcTemplate().query(&quot;select * from account where name = ?&quot;,new BeanPropertyRowMapper&lt;Account&gt;(Account.class),accountName);
        if(accounts.isEmpty()){
            return null;
        }
        if(accounts.size()&gt;1){
            throw new RuntimeException(&quot;结果集不唯一&quot;);
        }
        return accounts.get(0);
    }

    @Override
    public void updateAccount(Account account) {
        super.getJdbcTemplate().update(&quot;update account set name=?,money=? where id=?&quot;,account.getName(),account.getMoney(),account.getId());
    }
}
</code></pre>
<h4 id="5-beanxml配置步骤">(5) bean.xml配置步骤</h4>
<h5 id="第一步-配置事务管理器">第一步、配置事务管理器</h5>
<h5 id="第二步-配置事务的通知-transaction-manager标签引用事务管理器">第二步、配置事务的通知 transaction-manager标签引用事务管理器</h5>
<h5 id="第三步-配置事务的属性">第三步、配置事务的属性</h5>
<p><strong>read-only:</strong>  是否是只读事务。默认 false，不只读。<br>
<strong>isolation:</strong> 指定事务的隔离级别。默认值是使用数据库的默认隔离级别。<br>
<strong>propagation:</strong> 指定事务的传播行为。<br>
<strong>timeout:</strong> 指定超时时间。默认值为：-1。永不超时。<br>
<strong>rollback-for:</strong> 用于指定一个异常，当执行产生该异常时，事务回滚。产生其他异常，事务不回滚。<br>
没有默认值，任何异常都回滚。<br>
<strong>no-rollback-for</strong>: 用于指定一个异常，当产生该异常时，事务不回滚，产生其他异常时，事务回<br>
滚。没有默认值，任何异常都回滚。</p>
<h5 id="第四步-配置-aop-__配置切入点表达式">第四步、配置 AOP __配置切入点表达式</h5>
<h5 id="第五步-配置aop__配置切入点表达式和事务通知的对应关系">第五步、配置AOP__配置切入点表达式和事务通知的对应关系</h5>
<p><strong>总的配置如下</strong></p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xmlns:aop=&quot;http://www.springframework.org/schema/aop&quot;
       xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot;
       xsi:schemaLocation=&quot;
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd&quot;&gt;

    &lt;!--配置账户的业务层--&gt;
    &lt;bean id=&quot;accountService&quot; class=&quot;top.zoick.service.impl.AccountServiceImpl&quot;&gt;
        &lt;property name=&quot;accountDao&quot; ref=&quot;accountDao&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;

    &lt;!--配置账户的持久层--&gt;
    &lt;bean id=&quot;accountDao&quot; class=&quot;top.zoick.dao.impl.AccountDaoImpl&quot;&gt;
        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;



    &lt;!--配置数据源--&gt;
    &lt;bean id=&quot;dataSource&quot; class=&quot;org.springframework.jdbc.datasource.DriverManagerDataSource&quot;&gt;
        &lt;property name=&quot;driverClassName&quot; value=&quot;com.mysql.cj.jdbc.Driver&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;url&quot; value=&quot;jdbc:mysql://localhost:3306/eesy&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;username&quot; value=&quot;root&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;password&quot; value=&quot;root&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;

    &lt;!-- spring中基于XML的声明式事务控制配置步骤
       1、配置事务管理器
       2、配置事务的通知
               此时我们需要导入事务的约束 tx名称空间和约束，同时也需要aop的
               使用tx:advice标签配置事务通知
                   属性：
                       id：给事务通知起一个唯一标识
                       transaction-manager：给事务通知提供一个事务管理器引用
       3、配置AOP中的通用切入点表达式
       4、建立事务通知和切入点表达式的对应关系
       5、配置事务的属性
              是在事务的通知tx:advice标签的内部

    --&gt;
    &lt;!-- 配置事务管理器 --&gt;
    &lt;bean id=&quot;transactionManager&quot; class=&quot;org.springframework.jdbc.datasource.DataSourceTransactionManager&quot;&gt;
        &lt;!-- 注入 DataSource --&gt;
        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;

    &lt;!-- 配置事务的通知--&gt;
    &lt;tx:advice id=&quot;txAdvice&quot; transaction-manager=&quot;transactionManager&quot;&gt;
        &lt;!-- 配置事务的属性
                isolation：用于指定事务的隔离级别。默认值是DEFAULT，表示使用数据库的默认隔离级别。
                propagation：用于指定事务的传播行为。默认值是REQUIRED，表示一定会有事务，增删改的选择。查询方法可以选择SUPPORTS。
                read-only：用于指定事务是否只读。只有查询方法才能设置为true。默认值是false，表示读写。
                timeout：用于指定事务的超时时间，默认值是-1，表示永不超时。如果指定了数值，以秒为单位。

                rollback-for：用于指定一个异常，当产生该异常时，事务回滚，产生其他异常时，事务不回滚。没有默认值。表示任何异常都回滚。
                no-rollback-for：用于指定一个异常，当产生该异常时，事务不回滚，产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。
        --&gt;
        &lt;tx:attributes&gt;
            &lt;tx:method name=&quot;*&quot; propagation=&quot;REQUIRED&quot; read-only=&quot;false&quot;/&gt;
            &lt;!--下面优先级高--&gt;
            &lt;tx:method name=&quot;find*&quot; propagation=&quot;SUPPORTS&quot; read-only=&quot;true&quot;&gt;&lt;/tx:method&gt;
        &lt;/tx:attributes&gt;
    &lt;/tx:advice&gt;

    &lt;!-- 配置aop--&gt;
    &lt;aop:config&gt;
        &lt;!-- 配置切入点表达式--&gt;
        &lt;aop:pointcut id=&quot;pt1&quot; expression=&quot;execution(* top.zoick.service.impl.*.*(..))&quot;&gt;&lt;/aop:pointcut&gt;
        &lt;!--建立切入点表达式和事务通知的对应关系 --&gt;
        &lt;aop:advisor advice-ref=&quot;txAdvice&quot; pointcut-ref=&quot;pt1&quot;&gt;&lt;/aop:advisor&gt;
    &lt;/aop:config&gt;

&lt;/beans&gt;
</code></pre>
<h4 id="6-测试">(6) 测试</h4>
<pre><code class="language-java">/**
 * 使用Junit单元测试：测试我们的配置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = &quot;classpath:bean.xml&quot;)
public class AccountServiceTest {

    @Autowired
    private IAccountService as;

    @Test
    public  void testTransfer(){
        as.transfer(&quot;aaa&quot;,&quot;bbb&quot;,100f);

    }
}
</code></pre>
<h3 id="4-基于注解声明式事务控制的配置">4、基于注解声明式事务控制的配置</h3>
<h4 id="1-导入坐标-3">(1) 导入坐标</h4>
<h4 id="2-数据库与实体类-2">(2) 数据库与实体类</h4>
<p>同上</p>
<h4 id="3-创建业务层接口和实现类并使用注解让-spring-管理">(3) 创建业务层接口和实现类并使用注解让 spring 管理</h4>
<pre><code class="language-java">/**
* 账户的业务层实现类
*/
@Service(&quot;accountService&quot;)
public class AccountServiceImpl implements IAccountService {
	@Autowired
	private IAccountDao accountDao;
    
	//其余代码和基于 XML 的配置相同
}
</code></pre>
<h4 id="4-创建-dao-接口和实现类并使用注解让-spring-管理">(4) 创建 Dao 接口和实现类并使用注解让 spring 管理</h4>
<pre><code class="language-java">/**
 * 账户的持久层实现类
 */
@Repository(&quot;accountDao&quot;)
public class AccountDaoImpl  implements IAccountDao {


    @Autowired
    private JdbcTemplate jdbcTemplate;
    
	//其余代码和基于 XML 的配置相同
}
</code></pre>
<h4 id="5-beanxml配置步骤-2">(5) bean.xml配置步骤</h4>
<h5 id="第一步配置事务管理器并注入数据源">第一步：配置事务管理器并注入数据源</h5>
<pre><code class="language-xml">&lt;!-- 配置事务管理器 --&gt;
    &lt;bean id=&quot;transactionManager&quot; class=&quot;org.springframework.jdbc.datasource.DataSourceTransactionManager&quot;&gt;
        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;
</code></pre>
<h5 id="第二步-在业务层使用transactional-注解">第二步、在业务层使用@Transactional 注解</h5>
<pre><code class="language-java">@Service(&quot;accountService&quot;)
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true)//只读型事务配置
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao;


    @Override
    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);

    }

    //需要读写型事务
    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)
    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        System.out.println(&quot;transfer....&quot;);
            //2.1根据名称查询转出账户
            Account source = accountDao.findAccountByName(sourceName);
            //2.2根据名称查询转入账户
            Account target = accountDao.findAccountByName(targetName);
            //2.3转出账户减钱
            source.setMoney(source.getMoney()-money);
            //2.4转入账户加钱
            target.setMoney(target.getMoney()+money);
            //2.5更新转出账户
            accountDao.updateAccount(source);

//            int i=1/0;

            //2.6更新转入账户
            accountDao.updateAccount(target);
    }
}
</code></pre>
<h5 id="第三步在配置文件中开启-spring-对注解事务的支持">第三步：在配置文件中开启 spring 对注解事务的支持</h5>
<p>总的bean.xml如下</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
       xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
       xmlns:aop=&quot;http://www.springframework.org/schema/aop&quot;
       xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot;
       xmlns:context=&quot;http://www.springframework.org/schema/context&quot;
       xsi:schemaLocation=&quot;
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd&quot;&gt;


    &lt;!--配置spring创建容器要扫描的包--&gt;
    &lt;context:component-scan base-package=&quot;top.zoick&quot;&gt;&lt;/context:component-scan&gt;

    &lt;!--配置JdbcTemplate--&gt;
    &lt;bean id=&quot;jdbcTemplate&quot; class=&quot;org.springframework.jdbc.core.JdbcTemplate&quot;&gt;
        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;

    &lt;!--配置数据源--&gt;
    &lt;bean id=&quot;dataSource&quot; class=&quot;org.springframework.jdbc.datasource.DriverManagerDataSource&quot;&gt;
        &lt;property name=&quot;driverClassName&quot; value=&quot;com.mysql.cj.jdbc.Driver&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;url&quot; value=&quot;jdbc:mysql://localhost:3306/eesy&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;username&quot; value=&quot;root&quot;&gt;&lt;/property&gt;
        &lt;property name=&quot;password&quot; value=&quot;root&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;

    &lt;!-- spring中基于注解 的声明式事务控制配置步骤
        1、配置事务管理器
        2、开启spring对注解事务的支持
        3、在需要事务支持的地方使用@Transactional注解
     --&gt;
    &lt;!-- 配置事务管理器 --&gt;
    &lt;bean id=&quot;transactionManager&quot; class=&quot;org.springframework.jdbc.datasource.DataSourceTransactionManager&quot;&gt;
        &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;&gt;&lt;/property&gt;
    &lt;/bean&gt;

    &lt;!-- 开启spring对注解事务的支持--&gt;
    &lt;tx:annotation-driven transaction-manager=&quot;transactionManager&quot;&gt;&lt;/tx:annotation-driven&gt;
&lt;/beans&gt;
</code></pre>

              </div>
              <div class="toc-container">
                <ul class="markdownIt-TOC">
<li>
<ul>
<li><a href="#%E4%B8%80-spring-%E4%B8%AD%E7%9A%84-jdbctemplate">一、Spring 中的 JdbcTemplate</a>
<ul>
<li><a href="#1-jdbctemplate-%E6%A6%82%E8%BF%B0">1、 JdbcTemplate 概述</a></li>
<li><a href="#2-jdbctemplate-%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%88%9B%E5%BB%BA">2、JdbcTemplate 对象的创建</a></li>
<li><a href="#3-jdbctemplate-%E7%9A%84%E5%A2%9E%E5%88%A0%E6%94%B9%E6%9F%A5%E6%93%8D%E4%BD%9C">3、JdbcTemplate 的增删改查操作</a>
<ul>
<li><a href="#1-%E5%AF%BC%E5%85%A5%E5%9D%90%E6%A0%87">(1) 导入坐标</a></li>
<li><a href="#2-%E5%9C%A8-spring-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6beanxml%E4%B8%AD%E9%85%8D%E7%BD%AE-jdbctemplate">(2) 在 spring 配置文件bean.xml中配置 JdbcTemplate</a></li>
<li><a href="#3-%E6%9C%80%E5%9F%BA%E6%9C%AC%E7%9A%84crud%E4%BD%BF%E7%94%A8">(3) 最基本的CRUD使用</a></li>
<li><a href="#4-%E6%9F%A5%E8%AF%A2%E6%89%80%E6%9C%89%E6%93%8D%E4%BD%9C">(4) 查询所有操作</a></li>
<li><a href="#5-%E6%9F%A5%E8%AF%A2%E4%B8%80%E4%B8%AA">(5) 查询一个</a></li>
<li><a href="#6-%E6%9F%A5%E8%AF%A2%E8%BF%94%E5%9B%9E%E4%B8%80%E8%A1%8C%E4%B8%80%E5%88%97%E6%95%B0%E6%8D%AE">(6) 查询返回一行一列数据</a></li>
</ul>
</li>
<li><a href="#4-%E5%9C%A8-dao-%E4%B8%AD%E4%BD%BF%E7%94%A8-jdbctemplate">4、在 dao 中使用 JdbcTemplate</a>
<ul>
<li><a href="#1-%E5%AE%9E%E4%BD%93%E7%B1%BB">(1) 实体类</a></li>
<li><a href="#2-iaccountdao%E6%8E%A5%E5%8F%A3">(2) IAccountDao接口</a></li>
<li><a href="#3-%E7%AC%AC%E4%B8%80%E7%A7%8D%E6%96%B9%E6%B3%95%E5%AE%9E%E7%8E%B0%E7%B1%BB%E4%B8%AD%E5%AE%9A%E4%B9%89-jdbctemplate">(3) 第一种方法：实现类中定义 JdbcTemplate</a></li>
<li><a href="#4-%E7%AC%AC%E4%BA%8C%E7%A7%8D%E6%96%B9%E6%B3%95%E5%AE%9E%E7%8E%B0%E7%B1%BB%E7%BB%A7%E6%89%BF-jdbcdaosupport">(4) 第二种方法：实现类继承 JdbcDaoSupport</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#%E4%BA%8C-spring-%E4%B8%AD%E7%9A%84%E4%BA%8B%E5%8A%A1%E6%8E%A7%E5%88%B6">二、Spring 中的事务控制</a>
<ul>
<li><a href="#1-spring-%E4%BA%8B%E5%8A%A1%E6%8E%A7%E5%88%B6%E9%9C%80%E8%A6%81%E6%98%8E%E7%A1%AE%E7%9A%84">1、Spring 事务控制需要明确的</a></li>
<li><a href="#2-spring-%E4%B8%AD%E4%BA%8B%E5%8A%A1%E6%8E%A7%E5%88%B6%E7%9A%84-api-%E4%BB%8B%E7%BB%8D">2、Spring 中事务控制的 API 介绍</a>
<ul>
<li><a href="#1-platformtransactionmanager">(1) PlatformTransactionManager</a></li>
<li><a href="#2-transactiondefinition">(2) TransactionDefinition</a>
<ul>
<li><a href="#a-%E4%BA%8B%E5%8A%A1%E7%9A%84%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB">a、事务的隔离级别</a></li>
<li><a href="#b-%E4%BA%8B%E5%8A%A1%E7%9A%84%E4%BC%A0%E6%92%AD%E8%A1%8C%E4%B8%BA">b、事务的传播行为</a></li>
<li><a href="#c-%E8%B6%85%E6%97%B6%E6%97%B6%E9%97%B4">c、超时时间</a></li>
<li><a href="#d-%E6%98%AF%E5%90%A6%E6%98%AF%E5%8F%AA%E8%AF%BB%E4%BA%8B%E5%8A%A1">d、是否是只读事务</a></li>
</ul>
</li>
<li><a href="#3-transactionstatus">(3) TransactionStatus</a></li>
</ul>
</li>
<li><a href="#3-%E5%9F%BA%E4%BA%8E-xml-%E7%9A%84%E5%A3%B0%E6%98%8E%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%8E%A7%E5%88%B6%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F-%E9%87%8D%E7%82%B9">3、基于 XML 的声明式事务控制（配置方式 ）重点</a>
<ul>
<li><a href="#%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84">项目结构</a></li>
<li><a href="#1-%E5%AF%BC%E5%85%A5%E5%9D%90%E6%A0%87-2">(1) 导入坐标</a></li>
<li><a href="#2-%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%8E%E5%AE%9E%E4%BD%93%E7%B1%BB">(2) 数据库与实体类</a></li>
<li><a href="#3-%E4%B8%9A%E5%8A%A1%E5%B1%82%E6%8E%A5%E5%8F%A3%E5%92%8C%E5%AE%9E%E7%8E%B0%E7%B1%BB">(3) 业务层接口和实现类</a></li>
<li><a href="#4-%E6%8C%81%E4%B9%85%E5%B1%82%E6%8E%A5%E5%8F%A3%E4%B8%8E%E5%AE%9E%E7%8E%B0%E7%B1%BB">(4) 持久层接口与实现类</a></li>
<li><a href="#5-beanxml%E9%85%8D%E7%BD%AE%E6%AD%A5%E9%AA%A4">(5) bean.xml配置步骤</a>
<ul>
<li><a href="#%E7%AC%AC%E4%B8%80%E6%AD%A5-%E9%85%8D%E7%BD%AE%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86%E5%99%A8">第一步、配置事务管理器</a></li>
<li><a href="#%E7%AC%AC%E4%BA%8C%E6%AD%A5-%E9%85%8D%E7%BD%AE%E4%BA%8B%E5%8A%A1%E7%9A%84%E9%80%9A%E7%9F%A5-transaction-manager%E6%A0%87%E7%AD%BE%E5%BC%95%E7%94%A8%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86%E5%99%A8">第二步、配置事务的通知 transaction-manager标签引用事务管理器</a></li>
<li><a href="#%E7%AC%AC%E4%B8%89%E6%AD%A5-%E9%85%8D%E7%BD%AE%E4%BA%8B%E5%8A%A1%E7%9A%84%E5%B1%9E%E6%80%A7">第三步、配置事务的属性</a></li>
<li><a href="#%E7%AC%AC%E5%9B%9B%E6%AD%A5-%E9%85%8D%E7%BD%AE-aop-__%E9%85%8D%E7%BD%AE%E5%88%87%E5%85%A5%E7%82%B9%E8%A1%A8%E8%BE%BE%E5%BC%8F">第四步、配置 AOP __配置切入点表达式</a></li>
<li><a href="#%E7%AC%AC%E4%BA%94%E6%AD%A5-%E9%85%8D%E7%BD%AEaop__%E9%85%8D%E7%BD%AE%E5%88%87%E5%85%A5%E7%82%B9%E8%A1%A8%E8%BE%BE%E5%BC%8F%E5%92%8C%E4%BA%8B%E5%8A%A1%E9%80%9A%E7%9F%A5%E7%9A%84%E5%AF%B9%E5%BA%94%E5%85%B3%E7%B3%BB">第五步、配置AOP__配置切入点表达式和事务通知的对应关系</a></li>
</ul>
</li>
<li><a href="#6-%E6%B5%8B%E8%AF%95">(6) 测试</a></li>
</ul>
</li>
<li><a href="#4-%E5%9F%BA%E4%BA%8E%E6%B3%A8%E8%A7%A3%E5%A3%B0%E6%98%8E%E5%BC%8F%E4%BA%8B%E5%8A%A1%E6%8E%A7%E5%88%B6%E7%9A%84%E9%85%8D%E7%BD%AE">4、基于注解声明式事务控制的配置</a>
<ul>
<li><a href="#1-%E5%AF%BC%E5%85%A5%E5%9D%90%E6%A0%87-3">(1) 导入坐标</a></li>
<li><a href="#2-%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%8E%E5%AE%9E%E4%BD%93%E7%B1%BB-2">(2) 数据库与实体类</a></li>
<li><a href="#3-%E5%88%9B%E5%BB%BA%E4%B8%9A%E5%8A%A1%E5%B1%82%E6%8E%A5%E5%8F%A3%E5%92%8C%E5%AE%9E%E7%8E%B0%E7%B1%BB%E5%B9%B6%E4%BD%BF%E7%94%A8%E6%B3%A8%E8%A7%A3%E8%AE%A9-spring-%E7%AE%A1%E7%90%86">(3) 创建业务层接口和实现类并使用注解让 spring 管理</a></li>
<li><a href="#4-%E5%88%9B%E5%BB%BA-dao-%E6%8E%A5%E5%8F%A3%E5%92%8C%E5%AE%9E%E7%8E%B0%E7%B1%BB%E5%B9%B6%E4%BD%BF%E7%94%A8%E6%B3%A8%E8%A7%A3%E8%AE%A9-spring-%E7%AE%A1%E7%90%86">(4) 创建 Dao 接口和实现类并使用注解让 spring 管理</a></li>
<li><a href="#5-beanxml%E9%85%8D%E7%BD%AE%E6%AD%A5%E9%AA%A4-2">(5) bean.xml配置步骤</a>
<ul>
<li><a href="#%E7%AC%AC%E4%B8%80%E6%AD%A5%E9%85%8D%E7%BD%AE%E4%BA%8B%E5%8A%A1%E7%AE%A1%E7%90%86%E5%99%A8%E5%B9%B6%E6%B3%A8%E5%85%A5%E6%95%B0%E6%8D%AE%E6%BA%90">第一步：配置事务管理器并注入数据源</a></li>
<li><a href="#%E7%AC%AC%E4%BA%8C%E6%AD%A5-%E5%9C%A8%E4%B8%9A%E5%8A%A1%E5%B1%82%E4%BD%BF%E7%94%A8transactional-%E6%B3%A8%E8%A7%A3">第二步、在业务层使用@Transactional 注解</a></li>
<li><a href="#%E7%AC%AC%E4%B8%89%E6%AD%A5%E5%9C%A8%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E4%B8%AD%E5%BC%80%E5%90%AF-spring-%E5%AF%B9%E6%B3%A8%E8%A7%A3%E4%BA%8B%E5%8A%A1%E7%9A%84%E6%94%AF%E6%8C%81">第三步：在配置文件中开启 spring 对注解事务的支持</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>

              </div>
            </div>
          </article>
        </div>

        
          <div class="next-post">
            <div class="next">下一篇</div>
            <a href="https://ArtZoick.github.io//post/spring_3">
              <h3 class="post-title">
                Spring_3 AOP
              </h3>
            </a>
          </div>
        

        
          
            <link rel="stylesheet" href="https://unpkg.com/gitalk/dist/gitalk.css">
<script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>

<div id="gitalk-container"></div>

<script>

  var gitalk = new Gitalk({
    clientID: 'a2471d09bddb5be481ee',
    clientSecret: '05d427fb27f873cfabace27b9f042f2c7f23000f',
    repo: 'ArtZoick.github.io',
    owner: 'ArtZoick',
    admin: ['ArtZoick'],
    id: (location.pathname).substring(0, 49),      // Ensure uniqueness and length less than 50
    distractionFreeMode: false  // Facebook-like distraction free mode
  })

  gitalk.render('gitalk-container')

</script>

          

          
        

        <div class="site-footer">
  <a href="https://zhidao.baidu.com/question/1867120235416383707.html" target="_blank" onclick="alert('盲生，你发现了华点！');">学习强国</a>

<!--Tido对话插件-->
<script src="//code.tidio.co/5fh6jaqvluqj8jjuf5zlqrf5tlzpktnx.js"></script>
<style>
.text-popup {
    animation: textPopup 1s;
    color: red;
    user-select: none;
    white-space: nowrap;
    position: absolute;
    z-index: 99;
}
@keyframes textPopup {
    0%, 100% {
        opacity: 0;
    }
    5% {
        opacity: 1;
    }
    100% {
        transform: translateY(-50px);    
    }
}
</style>
<script type="text/javascript" src="https://cdn.bootcss.com/canvas-nest.js/1.0.1/canvas-nest.min.js">
!function(){function n(n,e,t){return n.getAttribute(e)||t}function e(n){return document.getElementsByTagName(n)}
function t(){var t=e("script"),o=t.length,i=t[o-1];return{l:o,z:n(i,"zIndex",-1),o:n(i,"opacity",.5),c:n(i,"color","0,0,0")
,n:n(i,"count",99)}}function o(){a=m.width=window.innerWidth||document.documentElement.clientWidth||document.body.clientWidt
h,c=m.height=window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight}function i(){r.clearRect
(0,0,a,c);var n,e,t,o,m,l;s.forEach(function(i,x){for(i.x+=i.xa,i.y+=i.ya,i.xa*=i.x>a||i.x<0?-1:1,i.ya*=i.y>c||i.y<0?-1:1,r.
fillRect(i.x-.5,i.y-.5,1,1),e=x+1;e<u.length;e++)n=u[e],null!==n.x&&null!==n.y&&(o=i.x-n.x,m=i.y-n.y,l=o*o+m*m,l<n.max&&(n===
y&&l>=n.max/2&&(i.x-=.03*o,i.y-=.03*m),t=(n.max-l)/n.max,r.beginPath(),r.lineWidth=t/2,r.strokeStyle="rgba("+d.c+","+(t+.2)+")
",r.moveTo(i.x,i.y),r.lineTo(n.x,n.y),r.stroke()))}),x(i)}var a,c,u,m=document.createElement("canvas"),d=t(),l="c_n"+d.l,r=m.
getContext("2d"),x=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.
oRequestAnimationFrame||window.msRequestAnimationFrame||function(n){window.setTimeout(n,1e3/45)},w=Math.random,y={x:null,y:nul
l,max:2e4};m.id=l,m.style.cssText="position:fixed;top:0;left:0;z-index:"+d.z+";opacity:"+d.o,e("body")[0].appendChild(m),o(),
window.οnresize=o,window.οnmοusemοve=function(n){n=n||window.event,y.x=n.clientX,y.y=n.clientY},window.οnmοuseοut=function(){y
.x=null,y.y=null};for(var s=[],f=0;d.n>f;f++){var h=w()*a,g=w()*c,v=2*w()-1,p=2*w()-1;s.push({x:h,y:g,xa:v,ya:p,max:6e3})}u=
s.concat([y]),setTimeout(function(){i()},100)}();
</script>

<!--鼠标点击-->
<!--富强-->
<script type="text/javascript"> 
/* 鼠标特效 */ 
var a_idx = 0; 
jQuery(document).ready(function($) { 
    $("body").click(function(e) { 
        var a = new Array("富强", "民主", "文明", "和谐", "自由", "平等", "公正" ,"法治", "爱国", "敬业", "诚信", "友善"); 
        var $i = $("<span/>").text(a[a_idx]); 
        a_idx = (a_idx + 1) % a.length; 
        var x = e.pageX, 
        y = e.pageY; 
        $i.css({ 
            "z-index": 999, 
            "top": y - 20, 
            "left": x, 
            "position": "absolute", 
            "font-weight": "bold", 
            "color": "#ff6651" 
        }); 
        $("body").append($i); 
        $i.animate({ 
            "top": y - 180, 
            "opacity": 0 
        }, 
        1500, 
        function() { 
            $i.remove(); 
        }); 
    }); 
}); 
</script>
<!--edn--富强--> | 
  <a class="rss" href="https://ArtZoick.github.io//atom.xml" target="_blank">RSS</a>
</div>

<script>
  hljs.initHighlightingOnLoad()

  let mainNavLinks = document.querySelectorAll(".markdownIt-TOC a");

  // This should probably be throttled.
  // Especially because it triggers during smooth scrolling.
  // https://lodash.com/docs/4.17.10#throttle
  // You could do like...
  // window.addEventListener("scroll", () => {
  //    _.throttle(doThatStuff, 100);
  // });
  // Only not doing it here to keep this Pen dependency-free.

  window.addEventListener("scroll", event => {
    let fromTop = window.scrollY;

    mainNavLinks.forEach((link, index) => {
      let section = document.getElementById(decodeURI(link.hash).substring(1));
      let nextSection = null
      if (mainNavLinks[index + 1]) {
        nextSection = document.getElementById(decodeURI(mainNavLinks[index + 1].hash).substring(1));
      }
      console.log('section.offsetHeight', section.offsetHeight);
      if (section.offsetTop <= fromTop) {
        if (nextSection) {
          if (nextSection.offsetTop > fromTop) {
            link.classList.add("current");
          } else {
            link.classList.remove("current");    
          }
        } else {
          link.classList.add("current");
        }
      } else {
        link.classList.remove("current");
      }
    });
  });

</script>

      </div>
    </div>
  </body>
</html>
